const fs = require('fs-extra')
const path = require('path')
const cheerio = require('cheerio')
const Fontmin = require('fontmin')

class FontsPlugin {
    static defaultOption = {
        document: 'index.html',
        production: true
    }

    constructor(option) {
        this.option = { ...FontsPlugin.defaultOption, ...option }
        this.originSize = 0
        this.compressedSize = 0
    }

    apply(compiler) {
        const { webpack } = compiler
        const { Compilation, sources } = webpack
        const { RawSource } = sources

        compiler.hooks.thisCompilation.tap('FontsPlugin', compilation => {
            compilation.hooks.processAssets.tapAsync(
                { name: 'FontsPlugin', stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE },
                (assets, callback) => {
                    if (!this.option.production) {
                        return callback()
                    }

                    const { outputOptions } = compilation
                    const { path: outputPath } = outputOptions

                    const html = assets[this.option.document]

                    if (!html) {
                        return callback(new Error(`[FontsPlugin] Not font document "${this.option.document}"`))
                    }

                    const innerText = cheerio.load(html.source())('body').text()
                    const text = this.cleanText(innerText)

                    if (!text) {
                        return callback(new Error(`[FontsPlugin] document "${this.option.document}" is empty`))
                    }

                    const tempDir = path.join(outputPath, '.fonts')

                    const tasks = Object.keys(assets).map(file => {
                        if (/\.(woff|woff2|eot|ttf|otf)$/i.test(file)) {
                            const tempFilename = path.join(tempDir, path.basename(file))

                            fs.outputFileSync(tempFilename, assets[file].source())

                            return this.compressFont(text, tempFilename, tempDir).then(info => {
                                const content = fs.readFileSync(tempFilename)

                                assets[file] = new RawSource(content)

                                console.log(`FontsPlugin optimize ${file}`);
                                console.log('  total characters', text.length)
                                console.log('  original size', this.fmtSize(info.originSize))
                                console.log('  optimized size', this.fmtSize(info.compressedSize))
                                console.log('')
                            })
                        }
                    })
                    Promise.all(tasks)
                        .then(() => callback())
                        .catch(err => callback(err))
                }
            )
        })
    }

    cleanText(text) {
        if (typeof text === 'string') {
            return Array.from(new Set(text.replace(/\s+/g, '').split(''))).join('')
        }
        return ''
    }

    fmtSize(size) {
        let n, u
        if (size >= 1024 * 1024) {
            n = size / 1024 / 1024
            u = 'MiB'
        } else if (size >= 1024) {
            n = size / 1024
            u = 'KiB'
        } else {
            n = size
            u = 'bytes'
        }
        return `${n.toFixed(2)} ${u}`
    }

    compressFont(text, filename, outputPath) {
        return new Promise((resolve, reject) => {
            const originSize = fs.statSync(filename).size

            new Fontmin()
                .use(Fontmin.glyph({ text }))
                .src(filename)
                .dest(outputPath)
                .run((err, files) => {
                    if (err) {
                        reject(err)
                    } else {
                        const compressedSize = fs.statSync(filename).size
                        resolve({
                            filename,
                            originSize,
                            compressedSize
                        })
                    }
                })
        })
    }
}

module.exports = FontsPlugin
